"use strict"
var stats, axisX, axisY, axisZ, orientationFinished, deviceorientationFinished, param, deviceOrientationData, currentScreenOrientation, triFrameIndex, textureList, fontList, group, curEffectType, isPerspective, isIOS, constraints, isEnterGame, picker, pickerRoot, fonts, textures, perspectiveCamera, orthographicCamera, fontStyleIndex, shapeStyleIndex, effectCall, flashTimer, renderer, scene, light, curColorDifferentValue, oriColorMap, curEffectIndex, isMobile, isFull, delayTimer, video, canvas, threeRoot, canvasjq, threeRootjq, context, btnAbout, btnEdit, btnFullScreen, errorText, loadBar, gamePanel, startPanel, wordEffect, aboutPanel, closeAbout, hideUiBox, hideUiText, isHideUi, hideUiToggle, useFrontCamera, cameraText, cameraBox, cameraToggle, editPanel, isEditPanelShow, curColorContent, isColorPickerShow, eyedropperImg, isEyedropper, curSuckColor, hasSuckColor, deviceType, cdWrapper, cdSlider, curMaskColor;
// var curQuaternion, curQuaternionArray;
window.onload = async function() {
    appStart();
}

function initThree() {
    initScene();
    initCamera();
    initRenderer();
    initLight();
    switchEffect();
    initStatus();
    // createGUI();
    rendererUpdate();
}

function resource(type, id) {
    switch (type) {
        case "texture":
            return textures[id];
        case "font":
            return fonts[id];
        default:
            return null;
    }
}

async function loadResources() {
    textureList = ["circle", "star"];
    fontList = ["rift_en", "rift_jp"];
    //load textures
    textures = {};
    for (let i = 0; i < textureList.length; i++) {
        new THREE.TextureLoader().load('./tex/' + textureList[i] + '.png', function(texture) {
            textures[textureList[i]] = texture;
        });
    }
    // load font
    fonts = {};
    for (let i = 0; i < fontList.length; i++) {
        new THREE.FontLoader().load('./json/' + fontList[i] + '.json', function(text) {
            fonts[fontList[i]] = text;
        });
    }
    console.log("resources load complete.");
}

function initStatus() {
    stats = new Stats();
    stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
    let st = $(stats.domElement);
    $("#kips").append(stats.domElement);
    st.css({ "z-index": "500", position: "absolute", left: 0, top: 0 });
}

//清理场景
function clearScene() {
    effectCall = null;
    if (scene) {
        while (scene.children.length > 0) {
            var obj = scene.children[0];
            if (obj instanceof THREE.Mesh) {
                disposeMesh(obj);
                scene.remove(obj);
            } else {
                if (obj instanceof THREE.Camera) {

                } else {
                    scene.remove(obj);
                }
            }
        }
    }
    if (group) {
        while (group.children.length > 0) {
            var obj = group.children[0];
            if (obj instanceof THREE.Mesh) {
                disposeMesh(obj);
                group.remove(obj);
            } else {
                if (obj instanceof THREE.Camera) {

                } else {
                    group.remove(obj);
                }
            }
        }
        group = null;
    }
}

//屏幕闪烁,类坏屏效果
function flashEffect(isDark) {
    group = null;
    group = new THREE.Group();
    curEffectType == "flash";
    scene.background = new THREE.Color(0xffffff);
    useOrthographicCamera();
    // var geometry = new THREE.Geometry(); //自定义几何体
    var meshList = [];
    let row = 6;
    let column = 8;
    let width = window.innerWidth / row;
    let height = window.innerHeight / column;
    let cmg = Utils.colorMatchGroup(isDark ? ColorTheme.HighContrastRatioDark : ColorTheme.HighContrastRatioLight);
    // console.log(cmg);
    let geometry = new THREE.PlaneGeometry(width, height);
    for (let i = 0; i < row; i++) {
        if (meshList[i] == null) {
            meshList[i] = [];
        }
        for (let j = 0; j < column; j++) {
            let material = new THREE.MeshBasicMaterial({
                side: THREE.BackSide,
                wireframe: false, //该参数的含义为显示线框
                opacity: 0.5,
                transparent: true,
            });
            var mesh = new THREE.Mesh(geometry, material);
            mesh.material.color = new THREE.Color(Utils.colorMatch(cmg, i * (j - 1) + j));
            mesh.material.opacity = isDark ? Utils.randomRange(0.6, 0.8) : Utils.randomRange(0.4, 0.6);
            mesh.position.set((i - row / 2 + 0.5) * width, (j - column / 2 + 0.5) * height, 0);
            group.add(mesh);
            meshList[i][j] = mesh;
        }
    }
    scene.add(group);
    if (flashTimer) {
        clearInterval(flashTimer);
    }
    flashTimer = setInterval(function() {
        for (let i = 0; i < meshList.length; i++) {
            for (let j = 0; j < meshList[i].length; j++) {
                let mesh = meshList[i][j];
                if (mesh instanceof THREE.Mesh) {
                    let r = parseInt(Utils.randomRange(500));
                    if (r < 100) {
                        mesh.material.color = new THREE.Color(Utils.colorMatch(cmg, i * (j - 1) + j));
                        for (let f = 0; f < mesh.geometry.faces.length; f++) {
                            mesh.scale.set(Utils.randomRange(1.5, 5), Utils.randomRange(1.5, 5), 1);
                            mesh.position.set((i - row / 2 + 0.5) * width + Utils.randomRange(-100, 100), (j - column / 2 + 0.5) * height + Utils.randomRange(-100, 100), 0);
                        }
                    }
                }
            }
        }
    }, 0);
}

// 创建运动方向
function createVelocity(isSnow) {
    // 默认向下
    let velocity = new THREE.Vector3(isSnow ? 0 : 1, isSnow ? -1.8 : -2, 0);
    rotateY(velocity, Utils.randomRange(isSnow ? 360 : 60));
    rotateX(velocity, Utils.randomRange(isSnow ? -45 : -15, isSnow ? 45 : 15));
    return velocity;
}

//星落和飘雪
function fallEffect(isSnow) {
    group = null;
    group = new THREE.Group();
    curEffectType == "fall";
    usePerspectiveCamera();
    let cmg = Utils.colorMatchGroup(ColorTheme.Winter);
    scene.background = new THREE.Color(isSnow ? cmg[parseInt(Utils.randomRange(1, cmg.length))] : 0x141414);
    var points;
    var range = 300; // 雪花出现范围
    let material = new THREE.PointsMaterial({
        map: isSnow ? resource("texture", "circle") : resource("texture", "star"), // 纹理
        transparent: true, // 透明
        size: 5,
        blending: THREE.AdditiveBlending,
        depthTest: false, //深度测试关闭，不消去场景的不可见面
        color: new THREE.Color(isSnow ? cmg[0] : 0xFFDC00),
    });
    // 通过自定义几何体设置粒子位置
    let geom = new THREE.Geometry();
    let min = -range / 2,
        max = range / 2;
    for (let i = 0; i < 80; i++) {
        // 随机生成雪花的位置
        let v = new THREE.Vector3(Utils.randomRange(min + 100, max - 100), Utils.randomRange(min + 50, max - 50), Utils.randomRange(min, max));
        // 随机生成雪花分别沿x、y、z轴方向移动速度
        v.velocity = createVelocity(isSnow);
        // 添加顶点
        geom.vertices.push(v);
    }

    points = new THREE.Points(geom, material);
    group.add(points)
    scene.add(group);
    effectCall = function() {
        var vertices = points.geometry.vertices;
        vertices.forEach(function(v, idx) {
            // 计算位置
            v.y += v.velocity.y;
            v.x += v.velocity.x;
            v.z += v.velocity.z;
            // 边界检查
            if (v.y <= -range / 2 + 50) v.y = range / 2;
            if (v.x <= -range / 2 + 100 || v.x >= range / 2 - 100) v.x = v.x * -1;
            if (v.z <= -range / 2 || v.z >= range / 2) v.velocity.z = v.velocity.z * -1;
        });
        points.geometry.verticesNeedUpdate = true; //渲染时需要更新位置（如果没有设为true,则无法显示动画）
    };
}

function shapeEffect(isMutiple) {
    group = null;
    group = new THREE.Group();
    curEffectType == "shape";
    usePerspectiveCamera();
    let cmg = Utils.colorMatchGroup();
    scene.background = new THREE.Color(cmg[0]);
    var meshList = [];
    var mesh;
    var isCircle = false;
    if (isMutiple) {
        var rectGeometry = new THREE.PlaneGeometry(30, 30);
        var triangleGeometry = new THREE.Geometry();
        var circleGeometry = new THREE.CircleGeometry(20, 30);
        for (let i = 0; i < 40; i++) {
            isCircle = false;
            var color = new THREE.Color(cmg[parseInt(Utils.randomRange(1, cmg.length))]);
            switch (i % 3) {
                case 0:
                    let material = new THREE.MeshBasicMaterial({
                        side: THREE.DoubleSide,
                        wireframe: false, //该参数的含义为显示线框
                        opacity: 0.5,
                        transparent: true,
                        color: color,
                    });
                    mesh = new THREE.Mesh(rectGeometry, material);
                    break;
                case 1:
                    mesh = createEquilateralTriangleMesh(triangleGeometry, color, 30, THREE.DoubleSide, false);
                    break;
                case 2:
                    isCircle = true;
                    mesh = createCircleMesh(circleGeometry, color);
                    break;
                default:
                    mesh = createEquilateralTriangleMesh(triangleGeometry, color, 30, THREE.DoubleSide, false);
                    break;
            }
            meshList[i] = new StochasticMotion(mesh, [-8, 8], [-8, 8], [8, 16], [-300, 300], [-300, 300], [-300, 300], [-0.2, 0.2], { isCircle: isCircle });
            mesh.position.set(meshList[i].px, meshList[i].py, meshList[i].pz);
            mesh.material.opacity = Utils.randomRange(0.7, 1);
            group.add(mesh);
        }
        scene.add(group);
        group.position.set(0, 0, -600);
    } else {
        shapeStyleIndex++;
        shapeStyleIndex %= 3;
        triFrameIndex++;
        triFrameIndex %= 2;
        var geometry = shapeStyleIndex == 2 ? new THREE.CircleGeometry(20, 30) : (shapeStyleIndex == 1 ? new THREE.PlaneGeometry(30, 30) : new THREE.Geometry()); //自定义几何体
        for (let i = 0; i < 50; i++) {
            let color = new THREE.Color(cmg[parseInt(Utils.randomRange(1, cmg.length))]);
            switch (shapeStyleIndex) {
                case 0:
                    mesh = createEquilateralTriangleMesh(geometry, color, 30, THREE.DoubleSide, triFrameIndex == 0);
                    break;
                case 1:
                    let material = new THREE.MeshBasicMaterial({
                        side: THREE.DoubleSide,
                        wireframe: false, //该参数的含义为显示线框
                        opacity: 0.5,
                        transparent: true,
                        color: color,
                    });
                    mesh = new THREE.Mesh(geometry, material);
                    break;
                case 2:
                    mesh = createCircleMesh(geometry, color);
                    break;
                default:
                    break;
            }
            meshList[i] = new StochasticMotion(mesh, [-8, 8], [-8, 8], [8, 16], [-300, 300], [-300, 300], [-300, 300], [-0.2, 0.2]);
            mesh.position.set(meshList[i].px, meshList[i].py, meshList[i].pz);
            mesh.material.opacity = Utils.randomRange(0.7, 1);
            group.add(mesh);
        }
        scene.add(group);
        group.position.set(0, 0, -600);
    }
    effectCall = function() {
        meshList.forEach(function(v, i) {
            let mesh = v.mesh
            mesh.position.set(mesh.position.x + v.sx, mesh.position.y + v.sy, mesh.position.z + v.sz);
            // mesh.rotation.y += v.rv;
            if (v.extra && v.extra.isCircle) {
                mesh.rotation.y += v.rv;
            } else {
                switch (v.axis) {
                    case 0:
                        mesh.rotation.y += v.rv;
                        break;
                    case 1:
                        mesh.rotation.z += v.rv;
                        break;
                    default:
                        mesh.rotation.y += v.rv;
                        break;
                }
            }
            if (mesh.position.z > 610) {
                v.reset();
                mesh.material.opacity = Utils.randomRange(0.7, 1);
                mesh.scale.set(Utils.randomRange(1, 2), Utils.randomRange(1, 2), 1);
            }
        });
    }
}

function cubeEffect() {
    group = null;
    group = new THREE.Group();
    // group.position.set(0, 0, -300);
    curEffectType == "cube";
    scene.add(light);
    let cmg = Utils.colorMatchGroup();
    scene.background = new THREE.Color(cmg[0]);
    usePerspectiveCamera();

    var meshList = [];
    var geometry = new THREE.BoxGeometry(15, 15, 15)
    for (let i = 0; i < 30; i++) {
        let color = new THREE.Color(cmg[parseInt(Utils.randomRange(1, cmg.length))]);
        var material = new THREE.MeshLambertMaterial({
            color: color,
            wireframe: false,
            transparent: true,
        });
        // material.color = color;
        var mesh = new THREE.Mesh(geometry, material);
        let px = Utils.randomRange(-1, 1) > 0 ? Utils.randomRange(800, 150) : Utils.randomRange(-800, -150);
        meshList[i] = {
            mesh: mesh,
            px: px,
            py: Utils.randomRange(-200, 200),
            pz: Utils.randomRange(-200, 200),
            sx: Utils.randomRange(6, 10) * (px > 0 ? -1 : 1),
            sy: Utils.randomRange(-3, 3),
            sz: Utils.randomRange(-3, 3),
            axis: parseInt(Utils.randomRange(3)) % 2,
            rv: Utils.randomRange(-0.1, 0.1),
            targetAlpha: Utils.randomRange(0.9, 1),
        };
        mesh.position.set(meshList[i].px, meshList[i].py, meshList[i].pz);
        let scale = Utils.randomRange(1, 2);
        mesh.scale.set(scale, scale, scale);
        group.add(mesh);
    }
    scene.add(group);
    group.position.set(0, 0, -270);
    effectCall = function() {
        meshList.forEach(function(v, i) {
            let mesh = v.mesh
            mesh.position.set(mesh.position.x + v.sx, mesh.position.y + v.sy, mesh.position.z + v.sz);
            if (mesh.material.opacity < v.targetAlpha) {
                mesh.material.opacity += 0.05;
            }
            // mesh.rotation.y += v.rv;
            switch (v.axis) {
                case 0:
                    mesh.rotation.y += v.rv;
                    break;
                case 1:
                    mesh.rotation.z += v.rv;
                    break;
                default:
                    mesh.rotation.y += v.rv;
                    break;
            }
            if (mesh.position.x > Utils.randomRange(200, 300) || mesh.position.x < -Utils.randomRange(200, 300)) {
                mesh.material.opacity = 0
                v.px = Utils.randomRange(-2, 2) > 0 ? Utils.randomRange(800, 200) : Utils.randomRange(-800, -200);
                v.py = Utils.randomRange(-200, 200);
                v.pz = Utils.randomRange(-200, 200);
                v.sx = Utils.randomRange(6, 10) * (v.px > 0 ? -1 : 1);
                v.sy = Utils.randomRange(-3, 3);
                v.sz = Utils.randomRange(-3, 3);
                mesh.position.set(v.px, v.py, v.pz);
                v.axis = parseInt(Utils.randomRange(3)) % 2;
                v.rv = Utils.randomRange(-0.1, 0.1);
                v.targetAlpha = Utils.randomRange(0.9, 1);
                // mesh.material.opacity = Utils.randomRange(0.7, 1);
                let scale = Utils.randomRange(1, 2);
                mesh.scale.set(scale, scale, scale);
            }
        });
    }
}

function charsEffect(bool) {
    group = null;
    group = new THREE.Group();
    curEffectType == "char";
    usePerspectiveCamera();
    let cmg = Utils.colorMatchGroup();
    scene.background = new THREE.Color(cmg[0]);
    var charList = [];
    // var language = bool ? "en" : (fontStyleIndex == 0 ? "jp" : "en");
    var str = bool ? letter : (fontStyleIndex == 0 ? japanesePhoneticAlphabet : "?");
    fontStyleIndex = bool ? fontStyleIndex : ((fontStyleIndex + 1) % 2);
    for (let i = 0; i < 20; i++) {
        var gem = new THREE.TextGeometry(str.substr(parseInt(Utils.randomRange(str.length)), 1), {
            size: bool ? 35 : 40, //字号大小，一般为大写字母的高度
            height: 0, //文字的厚度
            weight: 'normal', //值为'normal'或'bold'，表示是否加粗
            font: resource("font", bool ? "rift_en" : (fontStyleIndex == 0 ? "rift_en" : "rift_jp")),
            style: 'normal', //值为'normal'或'italics'，表示是否斜体
            bevelThickness: 1, //倒角厚度
            bevelSize: 1, //倒角宽度
            curveSegments: 2, //弧线分段数，使得文字的曲线更加光滑
            bevelEnabled: false, //布尔值，是否使用倒角，意为在边缘处斜切
        });
        gem.center();
        var mat = new THREE.MeshBasicMaterial({
            color: cmg[parseInt(Utils.randomRange(1, cmg.length))],
        });
        var mesh = new THREE.Mesh(gem, mat);
        charList[i] = new StochasticMotion(mesh, [-8, 8], [8, 16], [-8, 8], [-80, 80], [-200, -100], [110, -110], [-0.1, 0.1])
        mesh.position.set(charList[i].px, charList[i].py, charList[i].pz);
        group.add(mesh);
    }
    scene.add(group);
    group.position.set(0, 0, -250);
    effectCall = function() {
        charList.forEach(function(v, i) {
            let mesh = v.mesh
            mesh.position.set(mesh.position.x + v.sx, mesh.position.y + v.sy, mesh.position.z + v.sz);
            // mesh.rotation.y += v.rv;
            switch (v.axis) {
                case 0:
                    mesh.rotation.y += v.rv;
                    break;
                case 1:
                    mesh.rotation.z += v.rv;
                    break;
                default:
                    mesh.rotation.y += v.rv;
                    break;
            }
            if (mesh.position.y > 200) {
                v.reset();
                mesh.material.opacity = Utils.randomRange(0.7, 1);
                mesh.scale.set(Utils.randomRange(1, 2), Utils.randomRange(1, 2), 1);
            }
        });
    }
}

function initLight() {
    light = new THREE.PointLight(0xffffff);
    light.position.set(0, 10, 10);
}

//渲染器更新
function rendererUpdate() {
    stats.begin();
    if (curColorDifferentValue > 0) {
        if (effectCall) {
            effectCall();
        }
        renderer.render(scene, perspectiveCamera.visible ? perspectiveCamera : orthographicCamera);
    }
    // if (isPerspective && group) {
    // group.rotation.set(param.grx, param.gry, param.grz);
    // group.position.set(param.gpx, param.gpy, param.gpz);
    // perspectiveCamera.rotation.set(param.crx, param.cry, param.crz);
    // perspectiveCamera.position.set(param.cpx, param.cpy, param.cpz);
    // perspectiveCamera.fov = param.fov;
    // if (gravityFinished) {
    //     updateGravity();
    // }
    // }
    requestAnimationFrame(rendererUpdate); //循环调用animate函数
    stats.end();
}

function initScene() {
    scene = new THREE.Scene(); //场景
    scene.fog = new THREE.Fog(0xffffff, 1, 1000)
}

function usePerspectiveCamera() {
    perspectiveCamera.visible = true;
    orthographicCamera.visible = false;
    isPerspective = true;
}

function useOrthographicCamera() {
    perspectiveCamera.visible = false;
    orthographicCamera.visible = true;
    isPerspective = false;
}

function initCamera() {
    //透视相机,参数含义(可视角度,宽高比,近切面,远切面)
    //fov 可视角度 field of view
    perspectiveCamera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 10, 1000);
    perspectiveCamera.position.set(0, 0, 0);
    perspectiveCamera.lookAt(scene.position);
    orthographicCamera = new THREE.OrthographicCamera(window.innerWidth / -2, window.innerWidth / 2, window.innerHeight / -2, window.innerHeight / 2, -10, 10);
}

function initRenderer() {
    renderer = new THREE.WebGLRenderer({
        antialias: true,
    }); //渲染器
    renderer.setSize(window.innerWidth, window.innerHeight); //为渲染器设置尺寸
    //render.domElement本质上是一个HTML的canvas组件
    threeRoot.appendChild(renderer.domElement); //在body上添加子组件,即渲染器渲染的canvas
    if (curColorDifferentValue > 0) {
        renderer.render(scene, perspectiveCamera.visible ? perspectiveCamera : originalCamera);
    }
    $(renderer.domElement).css({ display: "block", position: "absolute", float: "left", top: "0", left: "0", "z-index": "0" });
    renderer.setClearColor(0x000000); //为渲染器设置颜色
}

//App入口
function appStart() {
    isEnterGame = false;
    let info = Utils.checkDeviceType(navigator.userAgent);
    isMobile = info.isMobile;
    isIOS = info.isIOS;
    // console.log(isMobile, isIOS);
    deviceType = isMobile ? "mobile" : "pc";
    initDomComponent(); //初始化组件和默认值
    initThree();
    initEffectText(); //初始化特效文字
    loadResources();
    //画布自适应
    canvasjq.css("opacity", "0");
    threeRootjq.css("opacity", "0");
    $(".bt").css("opacity", "0");
    startPanel.css("opacity", "0");
    loadBar.css({ display: "block", width: "0%" });
    //加载动画回调
    loadBar.animate({ width: "100%" }, 500, "easeOut", function() {
        loadBar.css({ display: "none", width: "0%" });
        // $(".bt").animate({ "opacity": "1" }, 400);
        btnAbout.animate({ opacity: "1" }, 400);
        btnFullScreen.animate({ opacity: "1" }, 400);
        startPanel.animate({ opacity: "1" }, 400);
    });
}

//初始化特效文字
function initEffectText() {
    var arr = [{
        text: DIMENSIONAL,
        trs: $("#t1"),
    }, {
        text: RIFT,
        trs: $("#t2"),
    }, {
        text: TAPTOSTART,
        trs: $("#t3"),
    }];
    var spanList = [];
    $.each(arr, function(index, cf) {
        var trs = cf.trs
        spanList[index] = spanList[index] || [];
        $.each(cf.text.split(''), function(i, v) {
            let str = v == " " ? "&nbsp;" : v;
            var span = $("<span class='st'>" + str + "</span>");
            span.oriWord = v;
            trs.append(span);
            spanList[index].push(span);
        });
    });
    var symbols = [];
    $.each(symbol.split(''), function(i, v) {
        symbols[i] = v;
    });

    // effect
    var timeStep = 80;
    wordEffect = setInterval(effect, timeStep)

    function effect() {
        let delay = 0;
        let num = parseInt(Utils.randomRange(8));
        timeStep = (num % 2 == 0 ? 20 : 0) + 80;
        clearInterval(wordEffect);
        wordEffect = setInterval(effect, timeStep);
        //core
        $.each(spanList, function(_, spans) {
            for (let index = 0; index < num; index++) {
                delay = index * 10;
                let tempTimer = setTimeout(function() {
                    let r = parseInt(Utils.randomRange(spans.length * 2));
                    if (spans[r] != undefined) {
                        let sr = r % 2 == 0 ? 1 : -1;
                        let lr = Utils.randompm();
                        spans[r].css({
                            top: parseInt(Utils.randomRange(80 * sr)) + "px",
                            left: parseInt(Utils.randomRange(80 * lr)) + "px",
                            transform: "scale(" + Utils.randomRange(1, 3) + ")",
                        });
                        spans[r].text(symbols[parseInt(Utils.randomRange(symbols.length - 1))]);
                        let timer = setTimeout(function() {
                            spans[r].css({
                                top: "0px",
                                left: "0px",
                                transform: "none",
                            });
                            if (spans[r].oriWord == " ") {
                                spans[r].html("&nbsp;");
                            } else {
                                spans[r].text(spans[r].oriWord);
                            }

                            clearTimeout(timer);
                        }, 50);
                    } else {
                        let r = parseInt(Utils.randomRange(spans.length * 2));
                        if (spans[r] != undefined) {
                            spans[r].css({
                                top: "0px",
                                left: "0px",
                                transform: "none",
                            });
                            spans[r].text("");
                            // 还原
                            let timer = setTimeout(function() {
                                if (spans[r].oriWord == " ") {
                                    spans[r].html("&nbsp;");
                                } else {
                                    spans[r].text(spans[r].oriWord);
                                }
                                clearTimeout(timer);
                            }, 60);
                        }
                    }
                    clearTimeout(tempTimer);
                }, delay)
            }
        });
    }
}

//tap to start
function enterGame() {
    gamePanel.css("display", "block");
    startPanel.css("display", "none");
    clearInterval(wordEffect);
    isEnterGame = true;
    requireCamera(requireCameraSucc);
    $(".bt").animate({ "opacity": isHideUi ? "0" : "1" }, 400);
    window.onresize = function() {
        let canvasAttr = { width: document.body.clientWidth, height: document.body.clientHeight };
        canvasjq.css(canvasAttr);
        threeRootjq.css(canvasAttr);
        calculateVideoCanvas();
        calculateLayout();
        //渲染器尺寸重置
        renderer.setSize(window.innerWidth, window.innerHeight); //重设渲染器宽高比
        if (perspectiveCamera.visible) {
            perspectiveCamera.aspect = window.innerWidth / window.innerHeight; //重设相机宽高比
            perspectiveCamera.updateProjectionMatrix(); // 重新计算投影矩阵
        }
        if (orthographicCamera.visible) {
            orthographicCamera.aspect = window.innerWidth / window.innerHeight; //重设相机宽高比
            orthographicCamera.updateProjectionMatrix(); // 重新计算投影矩阵
        }
    }
}

//请求相机成功回调
function requireCameraSucc() {
    canvasjq.animate({ opacity: "1" }, 400, function() {
        threeRootjq.css({ "background-color": "black", display: "block" });
        threeRootjq.animate({ opacity: "1" }, 400);
    });
    //将相机的画面绘制到画布上
    videoRenederToCanvas();
    // requireGravity();
}

//请求陀螺仪
// function requireGravity() {
//     if (isMobile) {
//         if (isIOS) {
//             // console.log("ios");
//             if (window.DeviceOrientationEvent && window.DeviceOrientationEvent["requestPermission"]) {
//                 window.DeviceOrientationEvent.requestPermission().then(state => {
//                     if (state === "granted") { //允许
//                         // alert("允许使用陀螺仪：", state);
//                     } else if (state === "denied") { //拒绝
//                         // alert("拒绝使用陀螺仪：", state);
//                     } else if (state === "prompt") {
//                         // alert("用户进行其他操作：", state);
//                         window.addEventListener('deviceorientation', function(event) {
//                             deviceOrientationData = event;
//                             gravityFinished = true;
//                         }, false);
//                     }
//                 });
//             } else {
//                 console.log("no gyroscope");
//             }
//         } else {
//             // console.log("android");
//             window.addEventListener('deviceorientation', function(event) {
//                 deviceOrientationData = event;
//                 gravityFinished = true;
//             }, false);
//         }
//         window.addEventListener('orientationchange', function() {
//             currentScreenOrientation = window.orientation;
//         }, false);
//     }
// }

// //匹配航空次序欧拉角的要素旋转矩阵
// function getBaseRotationMatrix(alpha, beta, gamma) {
//     var _x = bata ? beta * TO_RADIANS : 0; //beta value
//     var _y = gamma ? gamma * TO_RADIANS : 0; //gamma value
//     var _z = alpha ? alpha * TO_RADIANS : 0; //alpha value

//     var cX = Math.cos(_x);
//     var cY = Math.cos(_y);
//     var cZ = Math.cos(_z);
//     var sX = Math.sin(_x);
//     var sY = Math.sin(_y);
//     var sZ = Math.sin(_z);

//     var m11 = cZ * cY - sZ * sX * sY;
//     var m12 = -cX * sZ;
//     var m13 = cY * sZ * sX + cZ * sY;

//     var m21 = cY * sZ + cZ * sX * sY;
//     var m22 = cZ * cX;
//     var m23 = sZ * sY - cZ * cY * sX;

//     var m31 = -cX * sY;
//     var m32 = sX;
//     var m33 = cX * cY;

//     return [m11, m12, m13, m21, m22, m23, m31, m32, m33];
// }
// //构建屏幕方向变化矩阵
// function getScreenTransformationMatrix(screenOrientation) {
//     var orientationAngle = screenOrientation ? screenOrientation * TO_RADIANS : 0;
//     var cA = Math.cos(orientationAngle);
//     var sA = Math.sin(orientationAngle);
//     // Construct our screen transformation matrix 
//     var r_s = [
//         cA, -sA, 0,
//         sA, cA, 0,
//         0, 0, 1
//     ];
//     return r_s;
// }
// //世界方向矩阵
// function getWorldTransformationMatrix() {
//     var x = 90 * TO_RADIANS;
//     var cA = Math.cos(x);
//     var sA = Math.sin(x);
//     //Construct our world transformation matrix 
//     var r_w = [
//         1, 0, 0,
//         0, cA, -sA,
//         0, sA, cA
//     ];
//     return r_w;
// }

// function matrixMultiply(a, b) {
//     var final = [];
//     final[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
//     final[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
//     final[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];
//     final[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
//     final[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
//     final[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];
//     final[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
//     final[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
//     final[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];
//     return final;
// }

// function computeMatrix() {
//     var rotationMatrix = getBaseRotationMatrix(deviceOrientationData.alpha, deviceOrientationData.beta, deviceOrientationData.gamma); //R
//     var screenTransform = getScreenTransformationMatrix(currentScreenOrientation); //r_s
//     var screenAdjustedMatrix = matrixMultiply(rotationMatrix, screenTransform); //R_s
//     var worldTransform = getWorldTransformationMatrix(); //r_w
//     var finalMatrix = matrixMultiply(screenAdjustedMatrix, worldTransform); //R_w
//     return finalMatrix; //[m11,m12,m13,m21,m22,m23,m31,m32,m33]
// }

// function getBaseQuaternion(alpha, beta, gamma) {
//     var _x = beta ? beta - TO_RADIANS : 0; // beta value
//     var _y = gamma ? gamma * TO_RADIANS : 0; // gamma value
//     var _z = alpha ? alpha * TO_RADIANS : 0; // alpha value
//     var cX = Math.cos(_x / 2);
//     var cY = Math.cos(_y / 2);
//     var cZ = Math.cos(_z / 2);
//     var sX = Math.sin(_x / 2);
//     var sY = Math.sin(_y / 2);
//     var sZ = Math.sin(_z / 2);
//     var w = cX * cY * cZ - sX * sY * sZ;
//     var x = sX * cY * cZ - cX * sY * sZ;
//     var y = cX * sY * cZ + sX * cY * sZ;
//     var z = cX * cY * sZ + sX * sY * cZ;
//     return [w, x, y, z];
// }

// function getScreenTransformationQuaternion(screenOrientation) {
//     var orientationAngle = screenOrientation ? screenOrientation * TO_RADIANS : 0;
//     var minusHalfAngle = -orientationAngle / 2;
//     // Construct the screen transformation quaternion
//     var q_s = [Math.cos(minusHalfAngle), 0, 0, Math.sin(minusHalfAngle)];
//     return q_s;
// }

// function getWorldTransformationQuaternion() {
//     var worldAngle = 90 * TO_RADIANS;
//     var minusHalfAngle = -worldAngle / 2;
//     var q_w = [Math.cos(minusHalfAngle), Math.sin(minusHalfAngle), 0, 0];
//     return q_w;
// }

// function quaternionMultiply(a, b) {
//     var w = a[0] * b[0] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3];
//     var x = a[1] * b[0] + a[0] * b[1] + a[2] * b[3] - a[3] * b[2];
//     var y = a[2] * b[0] + a[0] * b[2] + a[3] * b[1] - a[1] * b[3];
//     var z = a[3] * b[0] + a[0] * b[3] + a[1] * b[2] - a[2] * b[1];

//     return [w, x, y, z];
// }

// function computeQuaternion() {
//     var quaternion = getBaseQuaternion(deviceOrientationData.alpha, deviceOrientationData.beta, deviceOrientationData.gamma); // q
//     var worldTransform = getWorldTransformationQuaternion(); // q_w
//     var worldAdjustedQuaternion = quaternionMultiply(quaternion, worldTransform); // q'_w
//     var screenTransform = getScreenTransformationQuaternion(currentScreenOrientation); // q_s
//     var finalQuaternion = quaternionMultiply(worldAdjustedQuaternion, screenTransform); // q'_s
//     return finalQuaternion; // [ w, x, y, z ]
// }

// function updateGravity() {
//     curQuaternionArray = computeQuaternion();
//     curQuaternion = new THREE.Quaternion().set(curQuaternionArray[0], curQuaternionArray[1], curQuaternionArray[2], curQuaternionArray[3]);
//     group.quaternion.slerp(curQuaternion, 0.01);
// }

function createGUI() {
    var paramObj = function() {
        this.fov = 75;
        this.grx = 0;
        this.gry = 0;
        this.grz = 0;
        this.gpx = 0;
        this.gpy = 0;
        this.gpz = 0;

        this.crx = 0;
        this.cry = 0;
        this.crz = 0;
        this.cpx = 0;
        this.cpy = 0;
        this.cpz = 0;
    }
    param = new paramObj();
    var gui = new dat.GUI;
    gui.add(param, "fov", 0, 180).name("视角大小");
    gui.add(param, "grx", -2, 2).name("grx");
    gui.add(param, "gry", -2, 2).name("gry");
    gui.add(param, "grz", -2, 2).name("grz");
    gui.add(param, "gpx", -1000, 1000).name("gpx");
    gui.add(param, "gpy", -1000, 1000).name("gpy");
    gui.add(param, "gpz", -1000, 1000).name("gpz");

    gui.add(param, "crx", -2, 2).name("crx");
    gui.add(param, "cry", -2, 2).name("cry");
    gui.add(param, "crz", -2, 2).name("crz");
    gui.add(param, "cpx", -1000, 1000).name("cpx");
    gui.add(param, "cpy", -1000, 1000).name("cpy");
    gui.add(param, "cpz", -1000, 1000).name("cpz");
    $(".dg").css("z-index", 500);
}

//初始化组件
function initDomComponent() {
    axisX = new THREE.Vector3(1, 0, 0).normalize();
    axisY = new THREE.Vector3(0, 1, 0).normalize();
    axisZ = new THREE.Vector3(0, 0, 1).normalize();
    triFrameIndex = 0;
    isPerspective = false;
    shapeStyleIndex = 0;
    fontStyleIndex = 0;
    curEffectIndex = 0;
    curColorDifferentValue = 0.4;
    oriColorMap = [];
    hasSuckColor = false;
    isEyedropper = false;
    isColorPickerShow = false;
    isEditPanelShow = false;
    useFrontCamera = false;
    isHideUi = false;
    isFull = false;
    startPanel = $("#start_panel");
    loadBar = $("#loading_bar");
    errorText = $("#error_text");
    errorText.css("z-index", "15");
    canvasjq = $("#canvas");
    threeRootjq = $("#three_root");
    threeRootjq.css("display", "none");
    gamePanel = $("#game_panel");
    video = document.getElementById('video');
    canvas = document.getElementById('canvas');
    threeRoot = document.getElementById('three_root');
    canvas.width = isMobile ? mobileCanvasWidth : canvasWidth;
    canvas.height = isMobile ? mobileCanvasHeight : canvasHeight;
    threeRoot.width = canvasWidth;
    threeRoot.height = canvasHeight;
    btnAbout = $("#bt_about");
    btnEdit = $("#bt_edit");
    btnFullScreen = $("#bt_full");
    aboutPanel = $("#about");
    closeAbout = $("#bt_close_about");
    editPanel = $("#edit_panel");
    btnFullScreen.click(function(event) {
        event.stopPropagation();
        fullScreen();
    });
    btnEdit.click(function(event) {
        event.stopPropagation();
        displayEditPanel();
    });
    btnAbout.click(function() {
        onAboutBtn();
    });
    startPanel.click(function() {
        enterGame();
    });
    closeAbout.click(function() {
        onCloseAbout();
    });
    hideUiBox = $("#hide_ui #box");
    hideUiText = $("#hide_ui #text");
    hideUiToggle = $("#hide_ui #inner_box");
    hideUiBox.click(function() {
        switchUiStatus();
    });
    hideUiText.click(function() {
        switchUiStatus();
    });

    cameraBox = $("#switch_camera #box");
    cameraText = $("#switch_camera #text");
    cameraToggle = $("#switch_camera #inner_box");
    cameraBox.click(function() {
        switchCameraToggle();
    });
    cameraText.click(function() {
        switchCameraToggle();
    });
    $("#edit_panel #first").click(function(event) {
        editBtn(1, event);
    });
    $("#edit_panel #second").click(function(event) {
        editBtn(2, event);
    });
    $("#edit_panel #third").click(function(event) {
        editBtn(3, event);
    });
    $("#edit_panel #last").click(function(event) {
        editBtn(4, event);
    });

    //colorpicker
    curColorContent = $("#edit_panel #third #color_disk");
    eyedropperImg = $("#edit_panel #second #bt_eyedropper");
    gamePanel.click(function(event) {
        onGamePanel(event);
    });
    picker = new Colorpicker({
        onchange: function(color) {
            // console.log(color);
            if (hasSuckColor) {
                hasSuckColor = false;
                curColorContent.css("background-color", curSuckColor);
            } else {
                curColorContent.css("background-color", "rgb(" + color.r + "," + color.g + "," + color.b + ")");
            }
            // var rgb = curColorContent.css("background-color").match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
            curMaskColor = color;
        },
        isMobile: isMobile,
    });
    pickerRoot = picker.getRoot();
    pickerRoot.css({ "z-index": "10" });
    curMaskColor = { r: 0, g: 0, b: 0 };
    //色差滑条
    cdWrapper = $("#edit_panel #cd_wrapper");
    cdSlider = $("#edit_panel #cd_slider");
    var wrapper = document.getElementById("cd_wrapper");
    var dragMove = function(e) {
        onColorDiffWrapper(e, wrapper);
    };
    //初始的色差
    cdSlider.css("width", curColorDifferentValue * 100 + "%");

    if (isMobile) {
        var passiveEvent = false;
        try {
            var opts = Object.defineProperty({}, 'passive', {
                get: function() {
                    passiveEvent = true;
                }
            });
            window.addEventListener("test", null, opts);
        } catch (e) {}
        passiveEvent = passiveEvent ? { capture: true, passive: true } : true;
        //移动端设备加触摸事件的css属性
        $(cdWrapper).css("touch-action", "auto");
        wrapper.addEventListener("touchstart", function(e) {
            onColorDiffWrapper(e, wrapper);
            document.addEventListener("touchmove", dragMove, passiveEvent);
        }, false);
        document.addEventListener("touchend", function(e) {
            document.removeEventListener("touchmove", dragMove, passiveEvent);
        }, false);
    } else {
        wrapper.addEventListener("mousedown", function(e) {
            onColorDiffWrapper(e, wrapper);
            document.addEventListener("mousemove", dragMove, false);
        }, false);
        document.addEventListener("mouseup", function(e) {
            document.removeEventListener("mousemove", dragMove, false)
        }, false);
    }

    // $("#version").text("ver 1.1");
}

//调整色差
function onColorDiffWrapper(e, element) {
    e.stopPropagation();
    var x = isMobile ? e.touches[0].clientX : e.x; //兼容移动端
    var xPage = x + document.body.scrollLeft;
    // var yPage = e.y + document.body.scrollTop;
    let e_x = locationLeft(element);
    // var e_y = locationTop(element);
    var xPos = xPage - e_x;
    // var yPos = yPage - e_y;
    // var hotspot = { x: xPos, y: yPos };// 当前点击位置
    var length = $(element).outerWidth();
    var value = (xPos - length * 0.1) / (length * 0.8); //0.8是css的缩放scale
    value = (value >= 0 && value <= 1) ? value : (value < 0 ? 0 : 1);
    cdSlider.css("width", value * 100 + "%");
    curColorDifferentValue = value;
}

// 处理点击主gamePanel的逻辑
function onGamePanel(event) {
    if (isEyedropper) {
        unactiveEyedropper();
        //取色
        if (context) {
            // 需要把实际的偏移坐标映射到canvas画布坐标上
            var pageX = Math.round((event.offsetX / window.innerWidth) * (isMobile ? mobileCanvasWidth : canvasWidth));
            var pageY = Math.round((event.offsetY / window.innerHeight) * (isMobile ? mobileCanvasHeight : canvasHeight));
            var index = parseInt((pageY - 1) * (isMobile ? mobileCanvasWidth : canvasWidth) + pageX);

            index *= 4;
            curMaskColor = { r: oriColorMap[index], g: oriColorMap[index + 1], b: oriColorMap[index + 2], a: oriColorMap[index + 3] };
            setColorToColorPanel(curMaskColor);
        } else {
            console.log("no canvas!");
        }
    } else {
        switchEffect();
    }
}

function switchEffect() {
    if (curColorDifferentValue == 0) {
        return;
    }
    clearScene();
    // do effect
    switch (curEffectIndex) {
        case Effects.FlashDark:
            flashEffect(true);
            break;
        case Effects.FlashLight:
            flashEffect();
            break;
        case Effects.CharsColorful:
            charsEffect(true);
            break;
        case Effects.CharsNormal:
            charsEffect();
            break;
        case Effects.CubeColorful:
            cubeEffect();
            break;
        case Effects.CubeNormal:
            cubeEffect();
            break;
        case Effects.SnowFall:
            fallEffect(true);
            break;
        case Effects.StarFall:
            fallEffect();
            break;
        case Effects.SingleShape:
            shapeEffect();
            break;
        case Effects.MutipleShape:
            shapeEffect(true);
            break;
    }
    curEffectIndex += 1;
    if (curEffectIndex == MaxEffects) {
        curEffectIndex = 0;
    }
}

// 将抓取的颜色设置给色板
function setColorToColorPanel(color) {
    var c = "rgba(" + color.r + "," + color.g + "," + color.b + "," + color.a + ")";
    curColorContent.css("background-color", c);
    hasSuckColor = true;
    curSuckColor = c;
    picker.setPointerByRGB(color.r, color.g, color.b);
}

// 取消edit面板的吸管工具激活态
function unactiveEyedropper() {
    isEyedropper = false;
    setImage(eyedropperImg, "./tex/eyedropper_normal.png");
}

//取消edit面板的色板工具激活态
function unactiveColorPicker() {
    isColorPickerShow = false;
    curColorContent.css("border-color", "transparent");
}

//编辑功能触发
function editBtn(btIndex, event) {
    //阻止事件冒泡
    event.stopPropagation();
    // console.log(btIndex);
    switch (btIndex) {
        case 1:
            if (parseFloat(editPanel.css("opacity")) < 1) {
                return;
            }
            if (isEditPanelShow) {
                hideEditPanel();
            }
            break;
        case 2:
            onEyedropperTool();
            break;
        case 3:
            onColorPicker();
            break;
        default:
            break;
    }
}

//吸管工具
function onEyedropperTool() {
    if (isColorPickerShow) {
        unactiveColorPicker();
        hideColorPanel();
    }
    isEyedropper = !isEyedropper;
    setImage(eyedropperImg, "./tex/eyedropper_" + (isEyedropper ? "active" : "normal") + ".png");

}

//调色板
function onColorPicker() {
    isColorPickerShow = !isColorPickerShow;
    curColorContent.css("border-color", isColorPickerShow ? "white" : "transparent");
    if (isEyedropper) {
        unactiveEyedropper();
    }
    if (isColorPickerShow) {
        displayColorPanel();
    } else {
        hideColorPanel();
    }
}

//隐藏色板
function hideColorPanel() {
    if (picker) {
        picker.hide();
    }
}

//显示色板
function displayColorPanel() {
    if (picker) {
        picker.show();
    }
}

//隐藏编辑界面
function hideEditPanel() {
    isEditPanelShow = !isEditPanelShow;
    if (!isEditPanelShow) {
        editPanel.animate({ opacity: "0" }, 200, function() {
            editPanel.css("display", "none");
        });
        btnEdit.css({ display: "block", opacity: "0" });
        if (!isHideUi) {
            btnEdit.animate({ opacity: "1" }, 200);
        }
    }
    unactiveEyedropper();
    unactiveColorPicker();
    hideColorPanel();
}

//编辑按钮
function displayEditPanel() {
    isEditPanelShow = !isEditPanelShow;
    if (isEditPanelShow) {
        editPanel.css({ display: "block", opacity: "0" });
        editPanel.animate({ opacity: "1" }, 200);
        btnEdit.animate({ opacity: "0" }, 200, function() {
            btnEdit.css("display", "none");
        });
    }
}

//切换UI的显隐状态
function switchUiStatus() {
    isHideUi = !isHideUi;
    hideUiToggle.css("display", isHideUi ? "inline-block" : "none");
    $(".bt").css("opacity", isHideUi ? "0" : "1");
}

//切换相机
function switchCameraToggle() {
    cameraToggle.css("display", cameraToggle.css("display") == "none" ? "inline-block" : "none");
}

//全屏
function fullScreen() {
    requestFullScreen(document.getElementById("kips"));
    let canvasAttr = { width: document.body.clientWidth, height: document.body.clientHeight };
    canvasjq.css(canvasAttr);
}

//全屏函数
function requestFullScreen(element) {
    var fullScreen = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullScreen;
    var quitFullScreenByDocument = document.exitFullScreen || document.mozCancelFullScreen || document.webkitExitFullscreen || element.msExitFullscreen;
    var quitFullScreenByElement = element.msExitFullscreen;
    if (!isFull) {
        fullScreen.call(element);
    } else {
        if (document) {
            quitFullScreenByDocument.call(document);
        } else {
            quitFullScreenByElement.call(element);
        }
    }
    isFull = !isFull;
}

//关于界面
function onAboutBtn() {
    aboutPanel.css({ display: "block", height: "0%" });
    aboutPanel.animate({ height: "100%" }, 100);
}
var videoStream;
//关闭关于界面
function onCloseAbout() {
    aboutPanel.css("height", "100%");
    aboutPanel.animate({ height: "0%" }, 100, function() {
        aboutPanel.css("display", "none");
    });
    let toggleActive = cameraToggle.css("display") != "none";
    if (isEnterGame) {
        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
        if (useFrontCamera) { //正在使用前置
            if (!toggleActive) { //需要切换至后置
                switchCamera(toggleActive);
            }
        } else { //当前后置
            if (toggleActive) { //需要切换至前置
                switchCamera(toggleActive);
            }
        }
    }
    useFrontCamera = toggleActive;
}

async function switchCamera(useFront) {
    stopVideoStream();
    constraints = {
        audio: false,
        video: {
            // width: {
            //     min: 640,
            //     ideal: isMobile ? 720 : 640,
            //     max: 3840,
            // },
            // height: {
            //     min: 480,
            //     ideal: isMobile ? 540 : 480,
            //     max: 2160,
            // },
            facingMode: useFront ? "user" : "environment",
        },
    };
    navigator.mediaDevices.getUserMedia(constraints).then(function(mdeiaStream) {
        videoStream = mdeiaStream;
        video.srcObject = videoStream;
        video.play();
    }).catch(function(error) {
        // console.log(error);
        displayError("Switch camera failure")
    });
}

function stopVideoStream() {
    if (videoStream) {
        videoStream.getTracks().forEach((track) => {
            track.stop();
        });
    }
}

//请求相机
function requireCamera(succCallback) {
    if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
        navigator.enumerateDevices = function(callback) {
            navigator.mediaDevices.enumerateDevices().then(callback);
        };
    }
    var isHTTPs = location.protocol === 'https:';
    var canEnumerate = false;
    if (typeof MediaStreamTrack !== 'undefined' && 'getSources' in MediaStreamTrack) {
        canEnumerate = true;
    } else if (navigator.mediaDevices && !!navigator.mediaDevices.enumerateDevices) {
        canEnumerate = true;
    }

    var hasWebcam = false;

    // var isMicrophoneAlreadyCaptured = false;
    var isWebcamAlreadyCaptured = false;

    function checkDeviceSupport(callback) {
        if (!canEnumerate) {
            return;
        }
        if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources) {
            navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack);
        }
        if (!navigator.enumerateDevices && navigator.enumerateDevices) {
            navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator);
        }
        if (!navigator.enumerateDevices) {
            if (callback) {
                callback();
            }
            return;
        }
        var MediaDevices = [];
        navigator.enumerateDevices(function(devices) {
            devices.forEach(function(_device) {
                var device = {};
                for (var d in _device) {
                    device[d] = _device[d];
                }
                if (device.kind === 'video') {
                    device.kind = 'videoinput';
                }
                var skip;
                MediaDevices.forEach(function(d) {
                    if (d.id === device.id && d.kind === device.kind) {
                        skip = true;
                    }
                });
                if (skip) {
                    return;
                }
                if (!device.deviceId) {
                    device.deviceId = device.id;
                }
                if (!device.id) {
                    device.id = device.deviceId;
                }
                if (!device.label) {
                    device.label = 'Please invoke getUserMedia once.';
                    if (!isHTTPs) {
                        device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.';
                    }
                } else {
                    if (device.kind === 'videoinput' && !isWebcamAlreadyCaptured) {
                        isWebcamAlreadyCaptured = true;
                    }
                }

                if (device.kind === 'videoinput') {
                    hasWebcam = true;
                }
                MediaDevices.push(device);
            });

            if (callback) {
                callback();
            }
        });
    }
    //检测相机是否通过
    checkDeviceSupport(function() {
        if (hasWebcam == false) {
            displayError("No cameras");
            return false;
        } else {
            if (isWebcamAlreadyCaptured == false) {
                displayError("Application for camera");
            }
        }
        constraints = {
            audio: false,
            video: {
                // width: {
                //     min: 640,
                //     ideal: isMobile ? 720 : 640,
                //     max: 3840,
                // },
                // height: {
                //     min: 480,
                //     ideal: isMobile ? 540 : 480,
                //     max: 2160,
                // },
                facingMode: useFrontCamera ? "user" : "environment",
            },
        };
        navigator.mediaDevices.getUserMedia(constraints).then(function(mediaStream) {
            // console.log(mediaStream);
            videoStream = mediaStream;
            video.srcObject = mediaStream;
            video.play();
            hideError();
            succCallback();
        }).catch(function(error) {
            displayError("No cameras are attached");
            console.log(error);
            navigator.permissions.query({
                name: 'camera'
            }).then(function(permissionStatus) {
                permissionStatus.onchange = function() {
                    location.reload();
                    if (permissionStatus.state == "granted") {
                        requireCameraSucc();
                    }
                };
            });
        });
    });
}

//隐藏erroc界面
function hideError() {
    loadBar.css("display", "none");
    errorText.text("");
}

//显示error界面
function displayError(errorMsg) {
    loadBar.css({ display: "block", width: "100%", "z-index": "15" });
    errorText.text(errorMsg != undefined ? errorMsg : "");
}

//video的数据流绘制到canvas上
var sx, sy, swidth, sheight, cwidth;

function videoRenederToCanvas() {
    $("#three_root,#canvas").css({ width: document.body.clientWidth, height: document.body.clientHeight });
    // $("#three_root").css(canvasAttr);
    context = canvas.getContext("2d"); //创建对象
    calculateVideoCanvas();
    if (delayTimer) {
        clearInterval(delayTimer);
    }
    delayTimer = setInterval(calculateLayout, 33);
    // $(video).css({ display: "block", float: "left", top: "0", left: "0", "z-index": "500", "visibility": "visible" });
}

function calculateVideoCanvas() {
    // if (isMobile) {}
    if (document.body.clientHeight / document.body.clientWidth >= 1.33333) {
        sx = (canvasWidth - (document.body.clientWidth * canvasHeight) / document.body.clientHeight) / 2
        sy = 0;
        swidth = (document.body.clientWidth * canvasHeight) / document.body.clientHeight;
        sheight = canvasHeight;
    } else {
        sx = 0;
        sy = (canvasHeight - (document.body.clientHeight * canvasWidth) / document.body.clientWidth) / 2;
        swidth = canvasWidth;
        sheight = (document.body.clientHeight * canvasWidth) / document.body.clientWidth;
    }
}

// 计算布局

function calculateLayout() {
    if (context) {
        context.drawImage(video, sx, sy, swidth, sheight, 0, 0, isMobile ? mobileCanvasWidth : canvasWidth, isMobile ? mobileCanvasHeight : canvasHeight); //剪切图像
        var img = context.getImageData(0, 0, canvas.width, canvas.height); //获取图片信息
        saveOriImgData(img.data);
        var newImgData = context.createImageData(canvas.width, canvas.height);
        cutPixel(newImgData, img.data, curMaskColor);

        context.putImageData(newImgData, 0, 0);
    }
    // new GlRenderer(canvas, 3000, video, this);
}

function saveOriImgData(data) {
    for (var i = 0, len = data.length; i < len; i++) {
        oriColorMap[i] = data[i];
    }
}

//裁剪色差达到colorDiff相似度的
function cutPixel(newImg, pixelData, rgbData) {
    for (var i = 0, len = pixelData.length; i < len; i += 4) {
        newImg.data[i] = pixelData[i];
        newImg.data[i + 1] = pixelData[i + 1];
        newImg.data[i + 2] = pixelData[i + 2];
        newImg.data[i + 3] = curColorDifferentValue == 0 ? pixelData[i + 3] : (labCheck(i, pixelData, rgbData) ? 0 : pixelData[i + 3]);
    }
}

//lab色差检测法
function labCheck(index, pixel, rgb) {
    return Utils.labDisSquare(pixel[index], pixel[index + 1], pixel[index + 2], rgb.r, rgb.g, rgb.b) <= curColorDifferentValue * curColorDifferentValue / 5;
}

//随机运动对象
function StochasticMotion(mesh, xSpeedRange, ySpeedRange, zSpeedRange, xPosRange, yPosRange, zPosRange, rotateSpeedRange, extra) {
    this.mesh = mesh;
    this.sx = Utils.randomRange(xSpeedRange[0], xSpeedRange[1]);
    this.sy = Utils.randomRange(ySpeedRange[0], ySpeedRange[1]);
    this.sz = Utils.randomRange(zSpeedRange[0], zSpeedRange[1]);
    this.px = Utils.randomRange(xPosRange[0], xPosRange[1]);
    this.py = Utils.randomRange(yPosRange[0], yPosRange[1]);
    this.pz = Utils.randomRange(zPosRange[0], zPosRange[1]);
    this.axis = parseInt(Utils.randomRange(3)) % 2;
    this.rv = Utils.randomRange(rotateSpeedRange[0], rotateSpeedRange[1]);
    this.extra = extra;
    this.xSpeedRange = xSpeedRange;
    this.ySpeedRange = ySpeedRange;
    this.zSpeedRange = zSpeedRange;
    this.xPosRange = xPosRange;
    this.yPosRange = yPosRange;
    this.zPosRange = zPosRange;
    this.rotateSpeedRange = rotateSpeedRange;
}
StochasticMotion.prototype.reset = function() {
    this.sx = Utils.randomRange(this.xSpeedRange[0], this.xSpeedRange[1]);
    this.sy = Utils.randomRange(this.ySpeedRange[0], this.ySpeedRange[1]);
    this.sz = Utils.randomRange(this.zSpeedRange[0], this.zSpeedRange[1]);
    this.px = Utils.randomRange(this.xPosRange[0], this.xPosRange[1]);
    this.py = Utils.randomRange(this.yPosRange[0], this.yPosRange[1]);
    this.pz = Utils.randomRange(this.zPosRange[0], this.zPosRange[1]);
    this.axis = parseInt(Utils.randomRange(3)) % 2;
    this.rv = Utils.randomRange(this.rotateSpeedRange[0], this.rotateSpeedRange[1]);
    this.mesh.position.set(this.px, this.py, this.pz);
}

//旋转X
function rotateX(velocity, t) {
    var cosRY = Math.cos(t * TO_RADIANS);
    var sinRY = Math.sin(t * TO_RADIANS);
    var o = velocity.y;
    var i = velocity.z;
    velocity.y = o * cosRY + i * sinRY;
    velocity.z = o * -sinRY + i * cosRY;
}

//旋转Y
function rotateY(velocity, t) {
    var cosRX = Math.cos(t * TO_RADIANS);
    var sinRX = Math.sin(t * TO_RADIANS);
    var o = velocity.x;
    var i = velocity.z;
    velocity.x = o * cosRX + i * sinRX;
    velocity.z = o * -sinRX + i * cosRX;
}

// 预加载
function setImage(imgObj, url) {
    const imgCache = new Image(); // 创建img对象给浏览器缓存图片
    imgCache.src = url;
    imgObj.attr("src", imgCache.src);
}

function disposeMesh(mesh) {
    if (mesh.geometry) {
        mesh.geometry.dispose();
    }
    if (mesh.material) {
        if (mesh.material instanceof THREE.MeshFaceMaterial) {
            $.each(mesh.material.materials, function(idx, mtrl) {
                if (mtrl.map) mtrl.map.dispose();
                if (mtrl.lightMap) mtrl.lightMap.dispose();
                if (mtrl.bumpMap) mtrl.bumpMap.dispose();
                if (mtrl.normalMap) mtrl.normalMap.dispose();
                if (mtrl.specularMap) mtrl.specularMap.dispose();
                if (mtrl.envMap) mtrl.envMap.dispose();
                if (mtrl.alphaMap) mtrl.alphaMap.dispose();
                if (mtrl.aoMap) mtrl.aoMap.dispose();
                if (mtrl.displacementMap) mtrl.displacementMap.dispose();
                if (mtrl.emissiveMap) mtrl.emissiveMap.dispose();
                if (mtrl.gradientMap) mtrl.gradientMap.dispose();
                if (mtrl.metalnessMap) mtrl.metalnessMap.dispose();
                if (mtrl.roughnessMap) mtrl.roughnessMap.dispose();

                mtrl.dispose();
            });
        } else {
            if (mesh.material.map) mesh.material.map.dispose();
            if (mesh.material.lightMap) mesh.material.lightMap.dispose();
            if (mesh.material.bumpMap) mesh.material.bumpMap.dispose();
            if (mesh.material.normalMap) mesh.material.normalMap.dispose();
            if (mesh.material.specularMap) mesh.material.specularMap.dispose();
            if (mesh.material.envMap) mesh.material.envMap.dispose();
            if (mesh.material.alphaMap) mesh.material.alphaMap.dispose();
            if (mesh.material.aoMap) mesh.material.aoMap.dispose();
            if (mesh.material.displacementMap) mesh.material.displacementMap.dispose();
            if (mesh.material.emissiveMap) mesh.material.emissiveMap.dispose();
            if (mesh.material.gradientMap) mesh.material.gradientMap.dispose();
            if (mesh.material.metalnessMap) mesh.material.metalnessMap.dispose();
            if (mesh.material.roughnessMap) mesh.material.roughnessMap.dispose();

            mesh.material.dispose();
        }
    }
}

//等边三角形mesh
function createEquilateralTriangleMesh(geometry, color, length, side, isWireframe) {
    let material = new THREE.MeshBasicMaterial({ color: color, side: side, wireframe: isWireframe || false, opacity: 1, transparent: true, });
    var d = (sr3 / 4) * length;
    var vertices = [
        new THREE.Vector3(-length / 2, -d, 0), //v0
        new THREE.Vector3(length / 2, -d, 0), //v1
        new THREE.Vector3(0, d, 0), //v2
    ];
    geometry.vertices = vertices;
    var faces = [
        new THREE.Face3(0, 1, 2),
    ];
    for (let index = 0; index < faces.length; index++) {
        faces[index].vertexColors[0] = color;
        faces[index].vertexColors[1] = color;
        faces[index].vertexColors[2] = color;
    }
    geometry.faces = faces;
    let mesh = new THREE.Mesh(geometry, material);
    mesh.geometry.colorsNeedUpdate = true;
    return mesh;
}

//圆形
function createCircleMesh(geometry, color) {
    return new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: color, side: THREE.DoubleSide, }));
}

// 找到元素的屏幕位置Left
function locationLeft(element) {
    var offsetTotal = element.offsetLeft;
    var scrollTotal = 0;
    if (element.tagName != "BODY") {
        if (element.offsetParent != null)
            return offsetTotal + scrollTotal + locationLeft(element.offsetParent);
    }
    return offsetTotal + scrollTotal;
}

// 找到元素的屏幕位置Top
function locationTop(element) {
    var offsetTotal = element.offsetTop;
    var scrollTotal = 0;
    if (element.tagName != "BODY") {
        if (element.offsetParent != null)
            return offsetTotal + scrollTotal + locationTop(element.offsetParent);
    }
    return offsetTotal + scrollTotal;
}